/*
 * SDLPongAudio.hpp
 *
 *  Created on: Jun 26, 2009
 *      Author: asantos
 */

#ifndef SDLPONGAUDIO_HPP_
#define SDLPONGAUDIO_HPP_


class SDLPongAudio{
private:
	SignalBroker& signalbroker;
	Signal<PongPhysics::CollisionHandler>& ballandleftwallcollision;
	Signal<PongPhysics::CollisionHandler>& ballandrightwallcollision;
	Signal<PongPhysics::CollisionHandler>& ballandtopwallcollision;
	Signal<PongPhysics::CollisionHandler>& ballandbottomwallcollision;
	Signal<PongPhysics::CollisionHandler>& ballandpaddle0collision;
	Signal<PongPhysics::CollisionHandler>& ballandpaddle1collision;
	Mix_Chunk* pingchunk;
	Mix_Chunk* pointscoredchunk;
	std::set<int> usedchannels;
public:
	SDLPongAudio(SignalBroker& signalbroker):
		signalbroker(signalbroker),
		ballandleftwallcollision(signalbroker.CreateOrGetExistingSignal<PongPhysics::CollisionHandler>("/physics/ballandleftwallcollision")),
		ballandrightwallcollision(signalbroker.CreateOrGetExistingSignal<PongPhysics::CollisionHandler>("/physics/ballandrightwallcollision")),
		ballandtopwallcollision(signalbroker.CreateOrGetExistingSignal<PongPhysics::CollisionHandler>("/physics/ballandtopwallcollision")),
		ballandbottomwallcollision(signalbroker.CreateOrGetExistingSignal<PongPhysics::CollisionHandler>("/physics/ballandbottomwallcollision")),
		ballandpaddle0collision(signalbroker.CreateOrGetExistingSignal<PongPhysics::CollisionHandler>("/physics/ballandpaddle0collision")),
		ballandpaddle1collision(signalbroker.CreateOrGetExistingSignal<PongPhysics::CollisionHandler>("/physics/ballandpaddle1collision"))
	{
		if(SDL_InitSubSystem(SDL_INIT_AUDIO) == -1){
		    // SDL Audio subsystem could not be started
			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("//log/output", "Could not init SDL_INIT_AUDIO");
			throw std::exception();
		}
		if(Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 1024) == -1){
		    // SDL_Mixer could not be started
			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "could not initialize SDL_mixer");
			throw std::exception();
		}

		Mix_AllocateChannels(16);
		for(int channel=0; channel<16; channel++){
			Mix_Volume(channel, MIX_MAX_VOLUME);
		}
		pingchunk = Mix_LoadWAV("ping.wav");
		if(!pingchunk){
			 // ping.wav could not be loaded
			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "could not load ping.wav");
			throw std::exception();
		}
		pointscoredchunk = Mix_LoadWAV("pointscored.wav");
		if(!pointscoredchunk){
			 // ping.wav could not be loaded
			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "could not load pointscoredchunk.wav");
			throw std::exception();
		}

		ballandleftwallcollision.connect(boost::bind(&SDLPongAudio::PlayPointScored, this));
		ballandrightwallcollision.connect(boost::bind(&SDLPongAudio::PlayPointScored, this));
		ballandtopwallcollision.connect(boost::bind(&SDLPongAudio::PlayPing, this));
		ballandbottomwallcollision.connect(boost::bind(&SDLPongAudio::PlayPing, this));
		ballandpaddle0collision.connect(boost::bind(&SDLPongAudio::PlayPing, this));
		ballandpaddle1collision.connect(boost::bind(&SDLPongAudio::PlayPing, this));

	}
	~SDLPongAudio(){

		//wait for all channels to be finished before freeing chunks
		std::set<int>::const_iterator itr = usedchannels.begin();
		for(;itr!=usedchannels.end(); itr++){
			while(Mix_Playing(*itr) != 0);
		}
		Mix_FreeChunk(pingchunk);
		Mix_FreeChunk(pointscoredchunk);

		/* Uninitialization */
		SDL_QuitSubSystem(SDL_INIT_AUDIO);
		Mix_CloseAudio();
	}

	void PlayPing(){
		int channel = Mix_PlayChannel(-1, pingchunk, 0);
		if(channel==-1) {
			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", Mix_GetError());
		}else{
			std::stringstream ss;
			ss<<"played ping.wav on channel"<<channel;
			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", (ss).str());
			usedchannels.insert(channel);
		}
	}
	void PlayPointScored(){
		int channel = Mix_PlayChannel(-1, pointscoredchunk, 0);
		if(channel==-1) {
			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", Mix_GetError());
		}else{
			usedchannels.insert(channel);
		}
	}
};

#endif /* SDLPONGAUDIO_HPP_ */
